// 蜂窝蜜造平台生成代码，如手工更改，请添加到 .beeignore 忽略生成

package com.fowo.api.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.lang.func.VoidFunc1;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.druid.sql.visitor.functions.If;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fowo.api.component.StartInit;
import com.fowo.api.datainterface.util.JsonUtil;
import com.fowo.api.datainterface.util.RedisUtil;
import com.fowo.api.entity.SupplierFlow;
import com.fowo.api.entity.SupplierFlowNode;
import com.fowo.api.model.supplier.flow.ContextSupplierFlow;
import com.fowo.api.model.supplier.flow.SupplierFlowItemVo;
import com.fowo.api.model.supplier.flow.SupplierFlowSearchParamPo;
import com.fowo.api.model.supplier.flow.form.relation.SupplierFlowFormRelationVo;
import com.fowo.api.model.supplier.flow.form.relation.log.SupplierFlowFormRelationLogItemVo;
import com.fowo.api.model.supplier.flow.node.SupplierFlowNodeEnum;
import com.fowo.api.model.supplier.flow.node.SupplierFlowNodeSelectDoUserParam;
import com.fowo.api.model.supplier.flow.node.SupplierFlowNodeSelectDoUserResult;
import com.fowo.api.model.supplier.msg.SupplierMsgVo;
import com.fowo.api.service.SupplierFlowNodeProcess;
import com.fowo.api.service.SupplierFlowNodeService;
import com.fowo.api.sys.model.SysDictAllItem;
import com.fowo.api.sys.model.SysUserVo;
import com.fowo.api.sys.service.impl.SysDictServiceImpl;
import com.fowo.api.sys.service.impl.SysUserServiceImpl;
import com.fowo.api.user.model.JwtUserInfo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import lombok.experimental.Accessors;
import lombok.experimental.FieldNameConstants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 供应商流程节点 服务实现类
 *
 * @author 蜂窝蜜造平台 配置人：任凯
 */
@Service
@Slf4j
@DS("ds24")
public class SupplierFlowNodeServiceImpl extends SupplierFlowNodeBaseServiceImpl
        implements SupplierFlowNodeService, SupplierFlowNodeProcess {
    @Resource
    private SupplierFlowFormRelationServiceImpl supplierFlowFormRelationService;
    @Resource
    private SupplierMsgServiceImpl supplierMsgService;
    @Resource
    private SupplierFlowServiceImpl supplierFlowService;

    /**
     * 获取节点
     *
     * @param enCode
     * @param node
     * @return
     */
    public SupplierFlowNode getNode(String enCode, String node) {
        SupplierFlowNode nodeEntity = this.getOne(Wrappers.<SupplierFlowNode>lambdaQuery().eq(SupplierFlowNode::getEnCode, enCode).eq(SupplierFlowNode::getNodeUp, StrUtil.blankToDefault(node, "0")), false);
        Assert.isTrue(ObjUtil.isNotEmpty(nodeEntity), "节点未配置!");
        return nodeEntity;
    }

    /**
     * 获取节点
     *
     * @param list
     * @throws Exception
     */
    public <T> SupplierFlowNode getConditionNode(List<T> list) throws Exception {
        T item = CollUtil.getFirst(list);
        SupplierFlowNode nodeEntity = this.getNode(Convert.toStr(ReflectUtil.getFieldValue(item, "enCode")), Convert.toStr(ReflectUtil.getFieldValue(item, "status")));
        return nodeEntity;
    }

    /**
     * 获取下一步执行人
     *
     * @param enCode
     * @param node
     * @return
     */
    @Override
    public List<SupplierFlowNodeSelectDoUserResult> getDoUser(String enCode, String node) {
        SupplierFlowNode nodeEntity = this.getNode(enCode, node);
        SupplierFlowNodeSelectDoUserParam param = new SupplierFlowNodeSelectDoUserParam();
        param.setId(nodeEntity.getId());
        List<SupplierFlowNodeSelectDoUserResult> supplierFlowNodeSelectDoUserResults = this.selectDoUser(param);
        return supplierFlowNodeSelectDoUserResults;
    }

    /**
     * 供应商流程节点处理
     *
     * @param list
     * @param nodeEntity
     * @param formStatus
     * @param verifyStatus
     * @param func1
     * @param <T>
     */
    @Transactional(rollbackFor = Exception.class)
    public <T> void process(List<T> list, SupplierFlowNode nodeEntity, String formStatus, List<String> verifyStatus, VoidFunc1<T> func1) {
        try {
            Assert.isTrue(CollUtil.isNotEmpty(list), "请选择对应单据!");
            formStatus = StrUtil.equals(formStatus, SupplierFlowNodeEnum.空.getValue()) ? null : formStatus;
            Assert.isTrue(StrUtil.isNotEmpty(nodeEntity.getType()), "节点类型未配置!");
            Assert.isTrue(StrUtil.isNotEmpty(formStatus), "表单状态为空!");
            SupplierFlowItemVo flowEntity = getSupplierFlow(nodeEntity);
            //回调字段处理
            String iddField = StrUtil.blankToDefault(flowEntity.getIdField(), "id");
            String statusdField = StrUtil.blankToDefault(flowEntity.getStatusField(), "status");
            String toUserdField = StrUtil.blankToDefault(nodeEntity.getToUserField(), "toUser");
            String forwardUserdField = StrUtil.blankToDefault(nodeEntity.getForwardUserField(), "forwardUser");
            String endTimeField = StrUtil.blankToDefault(flowEntity.getEndTimeField(), "endTime");
            String codeField = StrUtil.blankToDefault(flowEntity.getCodeField(), "code");
            String titleField = StrUtil.blankToDefault(flowEntity.getTitleField(), "title");

            if (CollUtil.isNotEmpty(verifyStatus)) {
                Assert.isTrue(list.stream().allMatch(a -> CollUtil.safeContains(verifyStatus, Convert.toStr(ReflectUtil.getFieldValue(a, statusdField)))), "校验异常,请检查单据状态!"); //1:草稿 2:已提交 3:已接受 4:执行中 5:已通过 6:已完成 -1:已撤回
            }
            for (T item : list) {
                ContextSupplierFlow context = new ContextSupplierFlow(flowEntity, nodeEntity, formStatus);
                context.setFormId(Convert.toLong(ReflectUtil.getFieldValue(item, iddField)));
                Assert.isTrue(ObjUtil.isAllNotEmpty(context.getFormId()), "表单业务主键字段{}为空!", iddField);
                context.setCode(Convert.toStr(ReflectUtil.getFieldValue(item, codeField)));
                Assert.isTrue(ObjUtil.isAllNotEmpty(context.getFormId()), "表单业务编号字段{}为空!", codeField);
                context.setEndTime(Convert.toDate(ReflectUtil.getFieldValue(item, endTimeField)));
                context.setTitle(Convert.toStr(ReflectUtil.getFieldValue(item, titleField)));
                ReflectUtil.setFieldValue(item, statusdField, formStatus);
                if (ObjUtil.isNotEmpty(func1)) {
                    func1.callWithRuntimeException(item);
                }
                if (CollUtil.safeContains(StrUtil.split(nodeEntity.getType(), ","), "4")) //1:普通节点  2:发起节点 3:结束节点 4:可拾取节点 5:可转发节点
                {
                    Assert.isTrue(ReflectUtil.hasField(item.getClass(), toUserdField), "节点拾取人字段{}不存在业务表!", toUserdField);
                    //派给的用户
                    context.setToUserList(Convert.toList(Long.class, ReflectUtil.getFieldValue(item, toUserdField)));
                    /**
                     * 截止时间
                     if (ReflectUtil.hasField(item.getClass(), endTimedField)) {
                     endTime = Convert.toDate(ReflectUtil.getFieldValue(item, endTimedField));
                     if (ObjUtil.isNotEmpty(endTime)) {
                     JSONObject obj = new JSONObject();
                     obj.put("id", formId);
                     obj.put("enCode", nodeEntity.getEnCode());
                     SpringUtil.getBean(StartInit.class).addQueue(obj, DateUtil.between(new Date(), endTime, DateUnit.SECOND), TimeUnit.SECONDS);
                     }
                     }
                     */
                }
                if (CollUtil.safeContains(StrUtil.split(nodeEntity.getType(), ","), "5")) {
                    if (ReflectUtil.hasField(item.getClass(), forwardUserdField)) {
                        Object forwardUserdValue = ReflectUtil.getFieldValue(item, forwardUserdField);
                        Assert.isTrue(ObjUtil.isNotEmpty(forwardUserdValue), "转发异常,未找到转发人!");
                        context.setForwardUser(Convert.toLong(forwardUserdValue));
                    }
                }
                //发送消息
                this.createMsg(context);
                //记录日志
                this.createRelation(context);
            }
        } catch (Exception ex) {
            log.error("发生异常,异常信息:" + StrUtil.subPre(ExceptionUtil.getRootCauseMessage(ex), 2000));
            throw new RuntimeException("发生异常,信息:" + StrUtil.subPre(ExceptionUtil.getRootCauseMessage(ex), 2000));
        }
    }


    /**
     * 队列回调
     */
    public void invokeEndTimeCallback() {
        //监听延迟队列
        StartInit.TaskEventListener<JSONObject> listener = new StartInit.TaskEventListener<>() {
            @Override
            public void invoke(JSONObject msg) {
                //这里调用你延迟之后的代码
                log.info("延迟队列执行...." + JsonUtil.getObjectToString(msg));
            }
        };
        SpringUtil.getBean(StartInit.class).getQueue(Object.class, listener);
    }

    /**
     * 发送通知
     *
     * @param context
     * @throws Exception
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void createMsg(ContextSupplierFlow context) throws Exception {
        List<SysDictAllItem> typeItems = SpringUtil.getBean(SysDictServiceImpl.class).getDictItems("SCM00195");
        SysDictAllItem SCM00195 = CollUtil.findOne(typeItems, i -> i.getItemValue().equals(context.getFlowEntity().getEnCode()));
        if (CollUtil.isNotEmpty(context.getToUserList())) {
            SupplierMsgVo supplierMsgVo = new SupplierMsgVo();
            for (Long userId : context.getToUserList()) {
                SysUserVo user = SpringUtil.getBean(SysUserServiceImpl.class).getVoById(userId);
                supplierMsgVo.setTitle(StrUtil.format("{},刚刚发送了{}通知", user.getName(), SCM00195.getItemText()));
                supplierMsgVo.setContent(StrUtil.format("任务编号:{}任务标题:{},通知时间:{}{},请及时查看!", context.getCode(), context.getTitle(), DateUtil.now(), String.format(",截止时间:{}", StrUtil.blankToDefault(DateUtil.format(context.getEndTime(), DatePattern.CHINESE_DATE_TIME_PATTERN), ""))));
                supplierMsgVo.setToUser(userId);
                supplierMsgVo.setEndTime(context.getEndTime());
                supplierMsgVo.setRemindType("1");
                supplierMsgVo.setFormId(context.getFormId());
                supplierMsgVo.setEnCode(context.getFlowEntity().getEnCode());
                supplierMsgVo.setStatus("1");
                supplierMsgVo.setRemark(null);
                supplierMsgService.create(supplierMsgVo);
            }
        }
        if (ObjUtil.isNotEmpty(context.getForwardUser())) {
            SupplierMsgVo supplierMsgVo = new SupplierMsgVo();
            SysUserVo user = SpringUtil.getBean(SysUserServiceImpl.class).getVoById(context.getForwardUser());
            supplierMsgVo.setTitle(StrUtil.format("{},刚刚转发了{}任务", user.getName(), SCM00195.getItemText()));
            supplierMsgVo.setContent(StrUtil.format("任务编号:{}任务标题:{},通知时间:{}{},请及时查看!", context.getCode(), context.getTitle(), DateUtil.now(), String.format(",截止时间:{}", StrUtil.blankToDefault(DateUtil.format(context.getEndTime(), DatePattern.CHINESE_DATE_TIME_PATTERN), ""))));
            supplierMsgVo.setToUser(context.getForwardUser());
            supplierMsgVo.setEndTime(context.getEndTime());
            supplierMsgVo.setRemindType("1");
            supplierMsgVo.setFormId(context.getFormId());
            supplierMsgVo.setEnCode(context.getNodeEntity().getEnCode());
            supplierMsgVo.setStatus("1");
            supplierMsgVo.setRemark(null);
            supplierMsgService.create(supplierMsgVo);
        }
    }


    /**
     * 创建供应商流程表单关系
     *
     * @param context
     * @throws Exception
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void createRelation(ContextSupplierFlow context) throws Exception {
        JwtUserInfo currentUser = JwtUserInfo.fromHeader();
        Assert.isTrue(ObjUtil.isNotEmpty(currentUser), "请先登录!");
        SupplierFlowFormRelationVo vo = supplierFlowFormRelationService.getVoById(supplierFlowFormRelationService.getIdByFormId(context.getFormId()));
        SupplierFlowFormRelationLogItemVo logVo = new SupplierFlowFormRelationLogItemVo();
        logVo.setEnCode(context.getNodeEntity().getEnCode());
        logVo.setFlowNodeId(context.getNodeEntity().getId());
        logVo.setFormId(context.getFormId());
        logVo.setFormStatus(context.getStatus());
        logVo.setDoUser(currentUser.getUserId());
        logVo.setCreateTime(new Date());
        logVo.setLastUpdateTime(new Date());
        if (currentUser != null) {
            logVo.setCreateUser(currentUser.getUserId());
            logVo.setLastUpdateUser(currentUser.getUserId());
        }
        if (ObjUtil.isNotEmpty(vo)) {
            //处理各种执行人
            if (CollUtil.safeContains(StrUtil.split(context.getNodeEntity().getType(), ","), "4") && CollUtil.isNotEmpty(context.getToUserList())) //1:普通节点  2:发起节点 3:结束节点 4:可拾取节点 5:可转发节点
            {
                //多人执行同一任务
                if (StrUtil.equals(context.getStatus(), vo.getFormStatus())) {
                    //待处理人
                    vo.setToUser(CollUtil.distinct(CollUtil.unionAll(vo.getToUser(), context.getToUserList())));
                } else {
                    //待处理人
                    vo.setToUser(context.getToUserList());
                }
            }
            if (CollUtil.safeContains(StrUtil.split(context.getNodeEntity().getType(), ","), "5") && ObjUtil.isNotEmpty(context.getForwardUser())) {
                //多人执行同一任务
                if (StrUtil.equals(context.getStatus(), vo.getFormStatus())) {
                    //转发人
                    vo.setForwardUser(CollUtil.distinct(CollUtil.unionAll(vo.getForwardUser(), CollUtil.newArrayList(context.getForwardUser()))));
                } else {
                    //转发人
                    vo.setForwardUser(CollUtil.newArrayList(context.getForwardUser()));
                }
            }
            //标题
            vo.setTitle(context.getTitle());
            //编号
            vo.setCode(context.getCode());
            //状态
            vo.setFormStatus(context.getStatus());
            //结束时间
            vo.setEndTime(context.getEndTime());
            //执行人
            vo.setDoUser(CollUtil.newArrayList(currentUser.getUserId()));
            //经办人
            vo.setOperatorUser(CollUtil.distinct(CollUtil.unionAll(vo.getOperatorUser(), CollUtil.newArrayList(currentUser.getUserId()))));
            //日志
            CollUtil.addIfAbsent(vo.getFlowFormRelation(), logVo);
            supplierFlowFormRelationService.update(vo);
        } else {
            vo = new SupplierFlowFormRelationVo();
            vo.setTitle(context.getTitle());
            vo.setCode(context.getCode());
            vo.setFlowNodeId(context.getNodeEntity().getId());
            vo.setEnCode(context.getNodeEntity().getEnCode());
            vo.setFormId(context.getFormId());
            vo.setFormStatus(context.getStatus());
            vo.setStartUser(CollUtil.newArrayList(currentUser.getUserId()));
            vo.setDoUser(CollUtil.newArrayList(currentUser.getUserId()));
            vo.setFlowFormRelation(CollUtil.newArrayList(logVo));
            supplierFlowFormRelationService.create(vo);
        }
    }

    /**
     * 获取实例
     *
     * @param nodeEntity
     * @return
     */
    protected SupplierFlowItemVo getSupplierFlow(SupplierFlowNode nodeEntity) {
        SupplierFlowSearchParamPo search = new SupplierFlowSearchParamPo();
        search.setEnCodeEq(nodeEntity.getEnCode());
        Page<SupplierFlowItemVo> supplierFlowItemVoPage = supplierFlowService.pageSearch(search);
        SupplierFlowItemVo flowEntity = CollUtil.getFirst(supplierFlowItemVoPage.getRecords());
        return flowEntity;
    }


}
